﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using RDPAddins.Common;
using System.IO;
using System.IO.Compression;
using System.Threading.Tasks;
using System.ComponentModel.Composition;
using System.Diagnostics;
using System.Threading;

namespace FileTransfer
{

    [AddinMetadata("File transfer", "FTFTA1",
        ChannelOptions.Compress | ChannelOptions.PriorityLow)]
    public partial class FileTransferAddin : UserControl, IAddin
    {
        public void Initialize(IChannel Channel)
        {
            this.Channel = Channel;
            Channel.Connected += new EventHandler(FileTransferAddin_Connected);
            Channel.Disconnected += new EventHandler(Channel_Disconnected);
            Channel.UI.BalloonTipClicked += new BalloonTipClickedHandler((ui, context) => StartFile((string)context));
            Channel.UI.MenuCreating += new MenuCreatingHandler(ui => mnuHolder);
            Channel.UI.IconCreating += new IconCreatingHandler(ui => Properties.Resources.AddinIcon);
            Channel.UI.ControlCreating += new ControlCreatingHandler(ui => this);
        }

        void Channel_Disconnected(object sender, EventArgs e)
        {
            if (stopWaiting != null)
            {
                stopWaiting.Cancel();
                stopWaiting = null;
            }
        }


        protected FileTransferAddin()
        {
            InitializeComponent();
        }

        IChannel Channel;
        CancellationTokenSource stopWaiting;

        void FileTransferAddin_Connected(object sender, EventArgs e)
        {
            stopWaiting = new CancellationTokenSource();
            Task.Factory.StartNew(token =>
            {
                while (true)
                {
                    try
                    {
                        string file = WaitForFile((CancellationToken)token);
                        NewFile(file);
                    }
                    catch (OperationCanceledException) { break; }
                    catch { }
                }
            }, stopWaiting.Token, stopWaiting.Token);
        }

        private void lvMain_MouseDoubleClick(object sender, MouseEventArgs e)
        {
            if (lvMain.SelectedItems.Count > 0)
            {
                StartFile(lvMain.SelectedItems[0].SubItems[1].Text);
            }
        }

        void StartFile(string filename)
        {
            if (!string.IsNullOrWhiteSpace(filename))
                Task.Factory.StartNew(() =>
                {
                    try { System.Diagnostics.Process.Start(filename); }
                    catch { }
                });
        }

        private void mnuLVCSaveAs_Click(object sender, EventArgs e)
        {
            if (lvMain.SelectedItems.Count > 0)
            {
                string fileSavePath = string.Empty;
                if (saveFileDialog.ShowDialog() == DialogResult.OK)
                {
                    fileSavePath = saveFileDialog.FileName;
                    try
                    {
                        System.IO.File.Copy(lvMain.SelectedItems[0].SubItems[1].Text, fileSavePath, true);
                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    }
                }
            }
        }

        private void mnuLVCOpen_Click(object sender, EventArgs e)
        {
            lvMain_MouseDoubleClick(null, null);
        }

        Dictionary<string, int> icons = new Dictionary<string, int>();

        int GetIconIndex(string filename, bool upload)
        {
            string ext = (upload ? "u" : "d") + Path.GetExtension(filename);
            int iconindex = 0;
            if (icons.ContainsKey(ext))
                iconindex = icons[ext];
            else
            {
                using (var icon = System.Drawing.Icon.ExtractAssociatedIcon(filename))
                {
                    var bmp = icon.ToBitmap();
                    using (var gr = Graphics.FromImage(bmp))
                    {
                        using (var img = upload ? Properties.Resources.Uploaded : Properties.Resources.Downloaded)
                        {
                            gr.DrawImage(img, 0, 19, 12, 12);
                        }
                    }
                    ilMain.Images.Add(bmp);
                    iconindex = ilMain.Images.Count - 1;
                    if (!ext.Contains("exe"))
                        icons[ext] = iconindex;
                }
            }
            return iconindex;
        }


        public void NewFile(string filename)
        {
            Channel.UI.DoOnUIThread(() => lvMain.Items.Add(new ListViewItem(new string[] { DateTime.Now.ToString(), filename }, GetIconIndex(filename, false))));
            System.Media.SystemSounds.Exclamation.Play();
            if (Properties.Settings.Default.AutoOpen)
                StartFile(filename);
            else
                Channel.UI.ShowBalloonTip(2000, Channel.Metadata.AddinName, filename, ToolTipIcon.Info, filename);
        }

        public string WaitForFile(CancellationToken token)
        {
            byte[] buffer = new byte[0x1000];
            int toreadhead = 12;
            int readedonce = 0;
            int readed = 0;
            while (toreadhead > 0)
            {
                readedonce = Channel.Read(buffer, readed, toreadhead);
                {
                    if (readedonce > 0)
                    {
                        readed += readedonce;
                        toreadhead -= readedonce;
                    }
                }
                token.ThrowIfCancellationRequested();
                Thread.Sleep(100);
            }
            long toread = BitConverter.ToInt64(buffer, 0);
            int toreadname = BitConverter.ToInt32(buffer, 8);
            readed = 0;
            while (toreadname > 0)
            {

                readedonce = Channel.Read(buffer, readed, toreadname);
                if (readedonce > 0)
                {
                    readed += readedonce;
                    toreadname -= readedonce;
                }
                token.ThrowIfCancellationRequested();
            }
            long maxread = toread;
            Channel.UI.DoOnUIThread(() =>
            {
                pbMain.Value = 0;
                pbMain.Visible = true;
            });
            string filename = Path.Combine(Path.GetTempPath(), System.Text.Encoding.UTF8.GetString(buffer, 0, readed));
            using (FileStream fs = File.OpenWrite(filename))
            {
                while (toread > 0)
                {
                    readedonce = Channel.Read(buffer, 0, buffer.Length);
                    if (readedonce > 0)
                    {
                        fs.Write(buffer, 0, readedonce);
                        toread -= readedonce;
                        Channel.UI.DoOnUIThread(()=> pbMain.Value = (int)(pbMain.Maximum * (maxread - toread) / (double)maxread));
                    }
                    token.ThrowIfCancellationRequested();
                }
            }
            Channel.UI.DoOnUIThread(() => pbMain.Visible = false);
            return filename;
        }

        private void mnuHSend_Click(object sender, EventArgs e)
        {
            btSend.Visible = false;
            mnuHSend.Enabled = false;
            string fileOpenPath = string.Empty;
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                fileOpenPath = openFileDialog.FileName;
                byte[] filename = System.Text.Encoding.UTF8.GetBytes(Path.GetFileName(fileOpenPath));
                Task.Factory.StartNew(() =>
                {
                    byte[] buffer = new byte[1600];
                    int bytesRead = 0;
                    using (FileStream fs = File.OpenRead(fileOpenPath))
                    {
                        long readed = 0, maxread = fs.Length;
                        Channel.Write(BitConverter.GetBytes(maxread), 0, 8);
                        Channel.Write(BitConverter.GetBytes(filename.Length), 0, 4);
                        Channel.Write(filename, 0, filename.Length);
                        Channel.UI.DoOnUIThread(() =>
                        {
                            pbMain.Value = 0;
                            pbMain.Visible = true;
                        });
                        while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) != 0)
                        {
                            Channel.Write(buffer, 0, bytesRead);
                            readed += bytesRead;
                            Channel.UI.DoOnUIThread(() => pbMain.Value = (int)(pbMain.Maximum * readed / (double)maxread));
                        }
                        Channel.UI.DoOnUIThread(() => pbMain.Visible = false);
                    }
                    Channel.UI.DoOnUIThread(() =>
                        {
                            lvMain.Items.Add(new ListViewItem(new string[] { DateTime.Now.ToString(), fileOpenPath }, GetIconIndex(fileOpenPath, true)));
                            btSend.Visible = true;
                            mnuHSend.Enabled = true;
                        });
                });
            }
            else
            {
                btSend.Visible = true;
                mnuHSend.Enabled = true;
            }
        }

        private void mnuHAuto_Click(object sender, EventArgs e)
        {
            var menu = (MenuItem)sender;
            menu.Checked = !menu.Checked;
            Properties.Settings.Default.AutoOpen = mnuHAuto.Checked;
            Properties.Settings.Default.Save();
        }
    }
}
